/* Orbas:
 *     A open source CORBA Specification implementation from Huihoo.
 *
 * Copyright 2002-2003 Huihoo.org, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */

package org.huihoo.orbas.orb;

/**
 * <p>Description: </p>
 * ORB main class.
 * <p>Copyright (c) 2002,2003</p>
 * <p>Company: <a href="http://www.huihoo.org/">huihoo.org</a></p>
 * @author <a href="http://www.huihoo.org/~mep">mep(mep@huihoo.com)</a>
 * @see <a href="http://www.huihoo.org/orbas">http://www.huihoo.org/orbas</a>
 * @version 1.0
 */

import org.huihoo.orbas.orb.iop.IopEngine;
import org.huihoo.orbas.orb.types.ObjRefDelegate;
import org.huihoo.orbas.orb.types.Any;
import com.cognition.util.j2me.DataTypeUtil;
import com.cognition.util.j2me.Logger;
import com.cognition.util.j2me.PersistedHashtable;
import java.util.*;
//import org.omg.PortableServer.POA;
import org.huihoo.orbas.poa.OrbasPoa;
import org.huihoo.orbas.orb.protocols.*;
import org.huihoo.orbas.orb.ior.IorParser;
import org.huihoo.orbas.orb.cdr.*;
import org.omg.IOP.IOR;
import org.huihoo.orbas.orb.thread.*;
import com.cognition.util.j2me.PersistedHashtable;
import com.cognition.util.j2me.SystemLogger;

/**
 * testcase test.org.huihoo.orbas.orb.TestORB
 */
public class ORB extends ORBSingleton {
    public static final int DEFAULT_GIOP_VERSION = 10;

    // TODO: initialize PersistedHashTable
    private PersistedHashtable properties = new PersistedHashtable("RootPOA");

    private Hashtable initialReferences = new Hashtable();
    private Hashtable valueFactories = new Hashtable();
    private static final String[] services = { "RootPOA", "NameService" };
    private java.util.Hashtable serverBroadcasterFactory = new Hashtable();

    private OrbasPoa rootPoa;
    private ProfileRegistry profileDealers;
    private TransportsPool pool;
    private IopEngine invoker;
    private RequestQueue queue;
    private ServerWorkerRegistry handler;
    private Listener listener;
    private ServerBroadcaster broadcaster;
    private java.lang.Thread casterThread;
    private java.util.Hashtable poaTable;

    private static Logger logger = SystemLogger.getLogger(SystemLogger.LOGGER_CONSOLE);
    public static final String LOG_TAG = "ORB";
    public ORB() {
    }

    public void initialize() {
        logger.debug(LOG_TAG, "initialize()");

        profileDealers = new ProfileRegistry();
        pool = new TransportsPool(this);

        org.huihoo.orbas.orb.protocols.iiop.IiopProtocolInitializer.setup(this);
        invoker = new IopEngine(this);

        queue = new RequestQueue(this, "ORBRequestQueue");
        handler = new ServerWorkerRegistry(this);
        listener = new Listener(this);
        poaTable = new java.util.Hashtable();

        rootPoa = null;
        //        rootPoa = new POA_impl(this, null, "RootPOA", null, null);
        //
        broadcaster = getServerBroadcaster(getDefautTransportProtocol());

    }

    public org.omg.CORBA.Object string_to_object(String str) {
        logger.debug(LOG_TAG, "string_to_object(...)");

        if (str == null || str.length() == 0)
            return null;

        IorParser ior = new IorParser(this, str);
        return ior.getObject();
    }

    public String object_to_string(org.omg.CORBA.Object obj) {
        logger.debug(LOG_TAG, "object_to_string(...)");

        if (obj == null) {
            return null;
        }
        ObjRefDelegate delegate =
            (ObjRefDelegate) ((org.omg.CORBA.portable.ObjectImpl)obj)
                ._get_delegate();

        String ior = delegate.toString();
        return ior;
    }

    public boolean get_service_information(
        short service_type,
        org.omg.CORBA.ServiceInformationHolder service_info) {
        logger.debug(LOG_TAG, "get_service_information(...)");

        //we have no service supported here
        return false;
    }

    public String[] list_initial_services() {
        logger.debug(LOG_TAG, "list_initial_services()");

        Vector v = new Vector();
        Enumeration e = initialReferences.keys();
        while (e.hasMoreElements()) {
            v.addElement(e.nextElement());
        }
        String[] initial_services = new String[services.length + v.size()];
        v.copyInto(initial_services);
        System.arraycopy(
            services,
            0,
            initial_services,
            v.size(),
            services.length);
        return initial_services;
    }

    public void register_initial_reference(
        String object_name,
        org.omg.CORBA.Object object)
        throws org.omg.CORBA.ORBPackage.InvalidName {
        logger.debug(LOG_TAG, "register_initial_reference(...)");

        if (object_name == null
            || object_name.length() == 0
            || initialReferences.containsKey(object_name)) {
            throw new org.omg.CORBA.ORBPackage.InvalidName();
        }
        else {
            initialReferences.put(object_name, object);
        }
    }

    public org.omg.CORBA.Object resolve_initial_references(String object_name)
        throws org.omg.CORBA.ORBPackage.InvalidName {
        
        
        logger.debug(LOG_TAG, "resolve_initial_references(...)");

        if (object_name == null || object_name.length() == 0)
            return null;


        if (initialReferences.containsKey(object_name)) {
            return (org.omg.CORBA.Object)initialReferences.get(object_name);
        }
        else if (object_name.equals("RootPOA")) {
            return (org.omg.CORBA.Object)getRootPOA();
        }
        //        else if (object_name.equals("NameService")) {
        //            return getNameServicePOA();
        //        }
        else {
            //get initial references from elsewhere
            org.omg.CORBA.Object obj = broadcaster.resolveBinding(object_name);

            if (obj == null) {
                throw new org.omg.CORBA.ORBPackage.InvalidName();
            }
            else {
                return obj;
            }
        }
    }

    public void run() {
        logger.debug(LOG_TAG, "run()");

        //implementation here
        java.lang.Thread handlerThread = new java.lang.Thread(handler);
        //        java.lang.Thread listenerThread = new java.lang.Thread(listener);
        handlerThread.start();

        //        listenerThread.start();

        listener.run();
        // new Thread(listener).start();

    }

    public void shutdown(boolean wait_for_completion) {

        logger.debug(LOG_TAG, "shutdown(...)");
        //do all works
        if (listener != null)
            listener.shutdown();

    }

    public void destroy() {
        logger.debug(LOG_TAG, "destroy()");
    }

    public org.omg.CORBA.Policy create_policy(int type, org.omg.CORBA.Any val)
        throws org.omg.CORBA.PolicyError {
        logger.debug(LOG_TAG, "create_policy(...)");

        //implementation here
        throw new org.omg.CORBA.NO_IMPLEMENT();
    }

    public org.omg.CORBA.portable.OutputStream create_output_stream() {
        logger.debug(LOG_TAG, "create_output_stream(...)");

        return new CDROutputStream(this);
    }

    protected void set_parameters(com.cognition.util.j2me.Properties props) {
        // TODO: process the values in props as well as the values on ORB.getProperties()
        //implementation here
        initialize();
    }

    /**
     * Get the Orbas Name Service from ior file.
     * @return org.omg.CORBA.Object
     */
    protected org.omg.CORBA.Object getNameServicePOA() {
        logger.debug(LOG_TAG, "getNameServicePOA()");

        org.omg.CORBA.Object nameObj = null;
        String iorProp = "orbas_naming.ior";

        byte[] buf = properties.get(iorProp);
        if (buf != null){

            String ior = new String(buf);

            org.omg.CORBA.Object obj = this.string_to_object(ior);
            //System.out.println("orb::obj="+obj);
            nameObj = obj;
        }


        return nameObj;
    }

    public org.omg.CORBA.Object get_value_def(String repid) {
        throw new org.omg.CORBA.NO_IMPLEMENT();
    }

    public org.omg.CORBA.portable.ValueFactory register_value_factory(
        String id,
        org.omg.CORBA.portable.ValueFactory factory) {
        //implementation here
        return (org.omg.CORBA.portable.ValueFactory)valueFactories.put(
            id,
            factory);
    }

    public void unregister_value_factory(String id) {
        //implementation here
        valueFactories.remove(id);
    }

    public org.omg.CORBA.portable.ValueFactory lookup_value_factory(
        String id) {
        //implementation here
        logger.debug(LOG_TAG, "lookup_value_factory(...)");

        org.omg.CORBA.portable.ValueFactory factory = null;
        factory = (org.omg.CORBA.portable.ValueFactory)valueFactories.get(id);

        if (factory == null) {
            if (id.startsWith("IDL")) {

                String baseName = id.substring(4, 
                        DataTypeUtil.lastIndexOf(id, ":"));

                baseName = baseName.replace('/', '.');

                String factoryName = baseName + "DefaultFactory";

                //load factory class
                try {
                	Class ins = Class.forName(factoryName);
                    factory =
                        (org
                            .omg
                            .CORBA
                            .portable
                            .ValueFactory) (ins.newInstance());
                    valueFactories.put(id, factory);
                }
                catch (ClassNotFoundException ec) {
                    logger.debug(LOG_TAG, ec.toString());
                }
                catch (java.lang.InstantiationException ei) {
                    logger.debug(LOG_TAG, ei.toString());
                }
                catch (java.lang.IllegalAccessException eill) {
                    logger.debug(LOG_TAG, eill.toString());
                }
            }
        }

        return factory;
    }

    public void set_delegate(java.lang.Object wrapper) {
        logger.debug(LOG_TAG, "set_delegate(" + wrapper.getClass().getName() + ")");

        //implementation here
        if (wrapper instanceof org.omg.PortableServer.Servant) {
            //set my own Delegate here

            //did we need multiple delegate instances?
            org.omg.PortableServer.Servant servant =
                (org.omg.PortableServer.Servant)wrapper;
            servant._set_delegate(new ServantDelegate(this));
        }
        else {
            throw new org.omg.CORBA.BAD_PARAM(
                0,
                org.omg.CORBA.CompletionStatus.COMPLETED_NO);
        }
    }

    public IorParser createIor(byte[] objectKey, String typeId) {
        //generating ior using default transport protocols
        logger.debug(LOG_TAG, "createIor(" + typeId + ")");

        org.omg.IOP.TaggedProfile[] profiles =
            profileDealers.getProfileCreator(
                getDefautTransportProtocol()).createProfiles(
                objectKey,
                getDefaultGiopVersion());

        IorParser parser = new IorParser(this, profiles, typeId);

        return parser;
    }

    public org.omg.CORBA.Any create_any() {
        //implementation here
        logger.debug(LOG_TAG, "create_any()");

        return new Any(this);
    }

    public OrbasPoa getRootPOA() {
        // TODO make RootPOA a parameterized class name read from properties but default to a known value.
        // load POA dynamically
        logger.debug(LOG_TAG, "getRootPOA()");

        if (rootPoa == null) {
            rootPoa = new org.huihoo.orbas.poa.POAImpl();
            rootPoa.setOrb(this);
            /*
             *
             // TODO: Make the class name configurable during runtime.
            try {

                rootPoa =
                    (OrbasPoa) (Class
                        .forName("org.huihoo.orbas.poa.POAImpl")
                        .newInstance());
            }
            catch (ClassNotFoundException ec) {
                System.out.println(ec.toString());
            }
            catch (java.lang.InstantiationException ei) {
                System.out.println(ei.toString());
            }
            catch (java.lang.IllegalAccessException eill) {
                System.out.println(eill.toString());
            }
            */
        }


        return rootPoa;

    }

    public ProfileRegistry getProfileRegistry() {
        return profileDealers;
    }

    public ClientWorker getDefaultClientWorker(int version) {
        return new org.huihoo.orbas.orb.giop.GiopClientWorker(this, version);
    }

    public ServerWorker getDefaultServerWorker(Transport t) {
        return new org.huihoo.orbas.orb.giop.GiopServerWorker(
            this,
            t,
            getDefaultGiopVersion());
    }

    private static ServerWorker oneThreadWorker;
    public ServerWorker getSingleThreadedServerWorker(RequestQueue queue){
        if (oneThreadWorker == null){
            oneThreadWorker = new org.huihoo.orbas.orb.giop.
                    GiopServerWorker(this, DEFAULT_GIOP_VERSION);
                    // GiopSingleThreadedServerWorker();
        }
        return oneThreadWorker;
    }

    public TransportsPool getTransportsPool() {
        return pool;
    }

    public int getDefaultGiopVersion() {
        return DEFAULT_GIOP_VERSION;
    }

    public int getDefautTransportProtocol() {
        return 0;
    };

    public IopEngine getDefaultIopEngine() {
        return invoker;
    }

    public RequestQueue getRequestQueue() {
        return queue;
    }

    public OrbasPoa locatePoa(String id) {
        //change
        return (OrbasPoa)poaTable.get(id);
    }

    public void registerPoa(OrbasPoa poa, String id) {
        poaTable.put(id, poa);
    }

    public void unregisterPoa(String id) {
        poaTable.remove(id);
    }

    public ServerBroadcaster getServerBroadcaster(int protocolId) {
        ServerBroadcaster ret;
        ret =
            (ServerBroadcaster)serverBroadcasterFactory.get(
                new Integer(protocolId));

        return ret;
    }

    public void registerServerBroadcaster(
        int protocolId,
        ServerBroadcaster caster) {
        serverBroadcasterFactory.put(new Integer(protocolId), caster);

    }

    public org.omg.CORBA.Object binding(
        String name,
        org.omg.PortableServer.Servant servant) {
        if (casterThread == null) {
            casterThread = new java.lang.Thread(broadcaster);
            casterThread.start();
        }
        logger.debug(LOG_TAG, "binding(...)");

        //    	System.out.println("Binding servant...");
        try {
            org.omg.PortableServer.POA poa =
                (org.omg.PortableServer.POA)getRootPOA();

            String[] ids = servant._all_interfaces(poa, null);
            org.omg.CORBA.Object ref = poa.servant_to_reference(servant);
            broadcaster.binding(name, ref);

            return ref;
        }
        catch (org.omg.PortableServer.POAPackage.ServantNotActive e) {
        }
        catch (org.omg.CORBA.ORBPackage.InvalidName e) {
        }
        catch (org.omg.PortableServer.POAPackage.WrongPolicy e) {
        }

        return null;
    }

    public org.omg.CORBA.Object resolve_binding(String name)
        throws org.omg.CORBA.ORBPackage.InvalidName {
        logger.debug(LOG_TAG, "resolve_binding(...)");

        org.omg.CORBA.Object obj = broadcaster.resolveBinding(name);

        //            return obj;
        if (obj == null) {
            throw new org.omg.CORBA.ORBPackage.InvalidName();
        }
        else {
            return obj;
        }
    }

}
